package invitation

import (
	"context"
	"encoding/json"
	"fmt"
	"invitation/internal/application"
	"invitation/internal/http/middleware"
	"invitation/internal/http/utils"
	"invitation/internal/model"
	"invitation/internal/pkg/ai/zpqy"
	"sync"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/google/uuid"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

const TotalHTTPTimeout = 360 * time.Second

// GenerateEndpoint 生成邀请函端点
func GenerateEndpoint(application *application.Application) func(ctx *fiber.Ctx) error {
	return func(ctx *fiber.Ctx) error {
		// 生成请求ID用于追踪
		requestID := uuid.New().String()
		logger := log.With().Str("requestID", requestID).Logger()

		userInput := ctx.Locals(middleware.LocalKeysInvitationGenerateRequest).(string)

		// 设置总超时
		requestCtx, cancel := context.WithTimeout(ctx.Context(), TotalHTTPTimeout)
		defer cancel()
		gameInfos, err := fetchDataWithHandler(
			application.RepoManager().GameInfoRepo().FindListAll,
			"Failed to fetch game list",
		)
		if err != nil {
			logger.Error().Err(err).Msg("Failed to fetch game list from repository")
			return handleErrorResponse(ctx, "Failed to fetch game list", err)
		}
		teacherInfos, err := fetchDataWithHandler(
			application.RepoManager().FirstTimeTeacherInfoRepo().FindListAll,
			"Failed to fetch teacher list",
		)
		if err != nil {
			logger.Error().Err(err).Msg("Failed to fetch teacher list from repository")
			return handleErrorResponse(ctx, "Failed to fetch teacher list", err)
		}
		// 2. 并行生成并获取AI选择（游戏和导师）
		logger.Debug().Msg("Step 2: Concurrently generating AI selections for games and teachers")
		z := application.ZhiPuQingYan()

		selections, err := generateSelectionsConcurrently(
			requestCtx,
			z,
			userInput,
			gameInfos,
			teacherInfos,
			&logger,
		)
		if err != nil {
			logger.Error().Err(err).Msg("Failed to generate selections")
			return handleErrorResponse(ctx, "Failed to generate selections", err)
		}

		// 3. 解析AI返回的选择
		logger.Debug().Msg("Step 3: Parsing AI selections")
		selectedGameIDs, err := parseGameSelection(selections.GameContent)
		if err != nil {
			logger.Error().Err(err).Msgf("Failed to unmarshal AI response for games. Cleaned: '%s'", selections.GameContent)
			return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
				"ok":      false,
				"message": "AI未能返回有效的游戏列表，请稍后重试或修改您的描述。",
			})
		}
		logger.Debug().Uints("selectedGameIDs", selectedGameIDs).Msg("Successfully parsed game names from AI response")

		selectedTeacherIDs, err := parseTeacherSelection(selections.TeacherContent)
		if err != nil {
			logger.Error().Err(err).Msgf("Failed to unmarshal AI response for teachers. Cleaned: '%s'", selections.TeacherContent)
			return ctx.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
				"ok":      false,
				"message": "AI未能返回有效的导师列表，请稍后重试或修改您的描述。",
			})
		}
		logger.Debug().Strs("selectedTeacherIDs", selectedTeacherIDs).Msg("Successfully parsed teacher IDs from AI response")

		// 4. 获取选中的游戏和导师详细信息
		logger.Debug().Msg("Step 4: Fetching detailed info for selected items")
		selectedGames, err := getDetailedGameInfo(application, selectedGameIDs)
		if err != nil {
			logger.Error().Err(err).Msg("Failed to get detailed game info")
			return handleErrorResponse(ctx, "Failed to get detailed game info", err)
		}
		logger.Debug().Int("detailedGameCount", len(selectedGames)).Msg("Successfully fetched detailed game info")

		selectedTeachers, err := getDetailedTeacherInfo(application, selectedTeacherIDs)
		if err != nil {
			logger.Error().Err(err).Msg("Failed to get detailed teacher info")
			return handleErrorResponse(ctx, "Failed to get detailed teacher info", err)
		}
		logger.Debug().Int("detailedTeacherCount", len(selectedTeachers)).Msg("Successfully fetched detailed teacher info")

		// 5. 并发生成结论
		logger.Debug().Msg("Step 5: Concurrently generating final conclusions")
		conclusions, err := generateConclusionsConcurrently(
			z,
			requestCtx,
			userInput,
			selectedGames,
			selectedTeachers,
			&logger,
		)
		if err != nil {
			logger.Error().Err(err).Msg("Failed to generate conclusions")
			return handleErrorResponse(ctx, "Failed to generate conclusions", err)
		}

		// 6. 拼接导师结论
		finalTeacherConclusion, err := generateFinalTeacherConclusion(conclusions.TeacherConclusions)
		if err != nil {
			logger.Error().Err(err).Msg("Failed to generate final teacher conclusion")
			return handleErrorResponse(ctx, "Failed to generate final teacher conclusion", err)
		}
		return ctx.Status(fiber.StatusOK).JSON(fiber.Map{
			"ok":                 true,
			"game_conclusion":    conclusions.GameConclusion,
			"teacher_conclusion": finalTeacherConclusion,
			"game_ids":           selectedGameIDs,
			"teacher_ids":        selectedTeacherIDs,
		})
	}
}

// SelectionResults 选择结果
type SelectionResults struct {
	GameContent    string
	TeacherContent string
}

// generateFinalTeacherConclusion 生成最终导师结论
func generateFinalTeacherConclusion(teacherConclusions []zpqy.TeacherConclusion) (string, error) {
	if len(teacherConclusions) == 0 {
		return "", fmt.Errorf("no teacher conclusions provided")
	}

	return zpqy.GenerateFinalTeacherConclusionPrompt(teacherConclusions)
}

type ConclusionsResult struct {
	GameConclusion     string
	TeacherConclusions []zpqy.TeacherConclusion
}

func parseTeacherConclusion(content string) (zpqy.TeacherConclusion, error) {
	var resp zpqy.TeacherConclusion
	if err := json.Unmarshal([]byte(content), &resp); err != nil {
		return zpqy.TeacherConclusion{}, fmt.Errorf("failed to parse teacher conclusion: %w", err)
	}
	return resp, nil
}

// generateSelectionsConcurrently 并发生成游戏和导师选择
func generateSelectionsConcurrently(
	ctx context.Context,
	z *zpqy.Client,
	userInput string,
	gameInfos []model.GameInfo,
	teacherInfos []model.FirstTimeTeacherInfo,
	logger *zerolog.Logger,
) (SelectionResults, error) {
	// 创建带超时的上下文
	ctx, cancel := context.WithTimeout(ctx, 60*time.Second)
	defer cancel()

	var wg sync.WaitGroup
	var mu sync.Mutex
	result := &SelectionResults{}
	var hasError bool
	var firstError error

	// 生成游戏选择
	wg.Add(1)
	go func() {
		defer wg.Done()
		defer func() {
			if r := recover(); r != nil {
				logger.Error().Msgf("Panic in game selection goroutine: %v", r)
				mu.Lock()
				hasError = true
				if firstError == nil {
					firstError = fmt.Errorf("internal server error in game selection")
				}
				mu.Unlock()
			}
		}()
		gameTempl, err := zpqy.GenerateGameSelectPrompt(zpqy.GameSelectData{
			UserInput: userInput,
			GameList:  joinToString(gameInfos, model.GameInfo.Template, ";"),
		})
		if err != nil {
			logger.Error().Err(err).Msg("Failed to generate game selection prompt")
			mu.Lock()
			hasError = true
			if firstError == nil {
				firstError = fmt.Errorf("failed to generate game selection prompt: %w", err)
			}
			mu.Unlock()
			return
		}

		resp, err := CallAIWithRetry(z, ctx, userInput, gameTempl, defaultResponseChecker, 5)
		if err != nil {
			logger.Error().Err(err).Msg("AI call failed for game selection")
			mu.Lock()
			hasError = true
			if firstError == nil {
				firstError = fmt.Errorf("AI request failed for game selection: %w", err)
			}
			mu.Unlock()
			return
		}

		mu.Lock()
		result.GameContent = utils.CleanAndFixJSON(resp.Choices[0].Message.Content)
		mu.Unlock()
	}()

	// 生成导师选择（修正错误信息）
	wg.Add(1)
	go func() {
		defer wg.Done()
		defer func() {
			if r := recover(); r != nil {
				logger.Error().Msgf("Panic in teacher selection goroutine: %v", r) // 修正
				mu.Lock()
				hasError = true
				if firstError == nil {
					firstError = fmt.Errorf("internal server error in teacher selection") // 修正
				}
				mu.Unlock()
			}
		}()
		teacherTempl, err := zpqy.GenerateTeacherSelectPrompt(zpqy.TeacherSelectData{
			UserInput:   userInput,
			TeacherList: joinToString(teacherInfos, model.FirstTimeTeacherInfo.Template, ";"),
		})
		if err != nil {
			logger.Error().Err(err).Msg("Failed to generate teacher selection prompt") // 修正
			mu.Lock()
			hasError = true
			if firstError == nil {
				firstError = fmt.Errorf("failed to generate teacher selection prompt: %w", err) // 修正
			}
			mu.Unlock()
			return
		}

		resp, err := CallAIWithRetry(z, ctx, userInput, teacherTempl, defaultResponseChecker, 5)
		if err != nil {
			logger.Error().Err(err).Msg("AI call failed for teacher selection") // 修正
			mu.Lock()
			hasError = true
			if firstError == nil {
				firstError = fmt.Errorf("AI request failed for teacher selection: %w", err) // 修正
			}
			mu.Unlock()
			return
		}

		mu.Lock()
		result.TeacherContent = utils.CleanAndFixJSON(resp.Choices[0].Message.Content)
		mu.Unlock()
	}()

	wg.Wait()

	if hasError {
		return *result, firstError
	}

	return *result, nil
}

// generateConclusionsConcurrently 并发生成游戏和导师结论
func generateConclusionsConcurrently(
	z *zpqy.Client,
	ctx context.Context,
	userInput string,
	games []zpqy.GameInfo,
	teachers []zpqy.TeacherInfo,
	logger *zerolog.Logger,
) (*ConclusionsResult, error) {
	// 添加限制，防止创建过多goroutine
	if len(teachers) > 10 {
		return nil, fmt.Errorf("too many teachers (%d), maximum allowed is 10", len(teachers))
	}

	var wg sync.WaitGroup
	var mu sync.Mutex
	result := &ConclusionsResult{}
	var hasError bool
	var firstError error

	// 生成游戏结论
	wg.Add(1)
	go func() {
		defer wg.Done()
		defer func() {
			if r := recover(); r != nil {
				logger.Error().Msgf("Panic in game conclusion goroutine: %v", r)
				mu.Lock()
				hasError = true
				firstError = fmt.Errorf("internal server error during game conclusion generation")
				mu.Unlock()
			}
		}()

		prompt, err := zpqy.GenerateGameConclusionPrompt(zpqy.GameConclusionData{
			UserInput:     userInput,
			SelectedGames: games,
		})
		if err != nil {
			logger.Error().Err(err).Msg("Failed to generate game conclusion prompt")
			mu.Lock()
			hasError = true
			if firstError == nil {
				firstError = fmt.Errorf("failed to generate game conclusion prompt: %w", err)
			}
			mu.Unlock()
			return
		}

		resp, err := CallAIWithRetry(z, ctx, userInput, prompt, defaultResponseChecker, 5)
		if err != nil {
			logger.Error().Err(err).Msg("AI call failed for game conclusion")
			mu.Lock()
			hasError = true
			if firstError == nil {
				firstError = fmt.Errorf("AI request failed for game conclusion: %w", err)
			}
			mu.Unlock()
			return
		}
		mu.Lock()
		result.GameConclusion = utils.CleanAndFixJSON(resp.Choices[0].Message.Content) // 添加清理
		mu.Unlock()
	}()

	// 生成教师结论（并发处理每个教师）
	teacherConclusions := make([]zpqy.TeacherConclusion, 0, len(teachers))
	for _, teacher := range teachers {
		wg.Add(1)
		go func(t zpqy.TeacherInfo) {
			defer wg.Done()

			// 添加重试机制
			maxRetries := 3
			var lastError error

			for retry := 0; retry < maxRetries; retry++ {
				var err error
				var resp *zpqy.Response
				var conclusion zpqy.TeacherConclusion

				// 捕获panic
				func() {
					defer func() {
						if r := recover(); r != nil {
							logger.Error().Msgf("Panic in teacher conclusion goroutine (attempt %d/%d): %v", retry+1, maxRetries, r)
							err = fmt.Errorf("internal server error during teacher conclusion generation: %v", r)
						}
					}()

					prompt, err := zpqy.GenerateSingleTeacherConclusionPrompt(zpqy.SingleTeacherConclusionData{
						UserInput: userInput,
						Teacher:   t,
					})
					if err != nil {
						logger.Error().Err(err).Str("teacherName", t.Name).Msgf("Failed to generate teacher conclusion prompt (attempt %d/%d)", retry+1, maxRetries)
						return
					}

					resp, err = CallAIWithRetry(z, ctx, userInput, prompt, defaultResponseChecker, 5)
					if err != nil {
						logger.Error().Err(err).Str("teacherName", t.Name).Msgf("AI call failed for teacher conclusion (attempt %d/%d)", retry+1, maxRetries)
						return
					}

					// 添加清理步骤
					cleanedContent := utils.CleanAndFixJSON(resp.Choices[0].Message.Content)
					conclusion, err = parseTeacherConclusion(cleanedContent)
					if err != nil {
						logger.Error().Err(err).Str("teacherName", t.Name).Msgf("Failed to parse teacher conclusion (attempt %d/%d)", retry+1, maxRetries)
						return
					}
				}()

				// 如果没有错误，说明成功，保存结果并退出重试循环
				if err == nil {
					mu.Lock()
					teacherConclusions = append(teacherConclusions, conclusion)
					mu.Unlock()
					return
				}

				// 记录错误，准备下一次重试
				lastError = err

				// 如果不是最后一次重试，等待一段时间再重试
				if retry < maxRetries-1 {
					waitTime := time.Duration(retry+1) * time.Second
					logger.Warn().Err(err).Str("teacherName", t.Name).Msgf("Retrying teacher conclusion generation in %v... (attempt %d/%d)", waitTime, retry+1, maxRetries)
					time.Sleep(waitTime)
				}
			}

			// 所有重试都失败了，记录错误
			logger.Error().Err(lastError).Str("teacherName", t.Name).Msgf("All retries exhausted for teacher conclusion generation")
			mu.Lock()
			hasError = true
			if firstError == nil {
				firstError = fmt.Errorf("failed to generate teacher conclusion for %s after %d attempts: %w", t.Name, maxRetries, lastError)
			}
			mu.Unlock()
		}(teacher)
	}

	wg.Wait()

	if hasError {
		return result, firstError
	}

	result.TeacherConclusions = teacherConclusions
	return result, nil
}
